package com.morgan.storageperlib

import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.Bitmap
import android.media.MediaScannerConnection
import android.media.MediaScannerConnection.OnScanCompletedListener
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore
import com.morgan.storageperlib.entity.*
import com.morgan.storageperlib.utils.Utils
import java.io.File
import java.io.FileNotFoundException
import java.util.*

/**
 *description：处理sdcard媒体文件 如/mnt/sdcard/Pictures、/mnt/sdcard/Movies、/mnt/sdcard/DCIM、/mnt/sdcard/Documents
 * https://developer.android.google.cn/reference/android/provider/MediaStore
 *<br>author：caowugao
 *<br>time：2021/3/1 0001 18:04
 */
class MediaStoreManager private constructor(){

    private val context : Context by lazy { Utils.context }
    private class InstanceHolder private constructor(){
        companion object {
            internal val INSTANCE : MediaStoreManager by lazy { MediaStoreManager() }
        }
    }

    companion object {
        fun getInstance() = InstanceHolder.INSTANCE
    }


    /**
     * @features 功    能：查询音频文件
     * @method 方法名：queryAudioItems
     * @param context
     * @return List<AudioItem>
    </AudioItem> */
    fun queryAudioItems(): List<AudioItem> {
        val list: MutableList<AudioItem> = ArrayList()
        val uri = AudioDBConstant.URI
        val clumnId = AudioDBConstant._ID
        val clumnTitle = AudioDBConstant.TITLE
        val clumnDisplayName = AudioDBConstant.DISPLAY_NAME
        val clumnData = AudioDBConstant.DATA
        val clumnMimeType = AudioDBConstant.MIME_TYPE
        val clumnSize = AudioDBConstant.SIZE
        val clumnYear = AudioDBConstant.YEAR
        val clumnDuration = AudioDBConstant.DURATION
        val clumnArtist = AudioDBConstant.ARTIST
        val clumnAlbum = AudioDBConstant.ALBUM
        val clumnDateAdded = AudioDBConstant.DATE_ADDED
        val clumnDateModified = AudioDBConstant.DATE_MODIFIED
        val cursor = context.contentResolver.query(
            uri, arrayOf(
                clumnId,
                clumnTitle,
                clumnDisplayName,
                clumnMimeType,
                clumnSize,
                clumnYear,
                clumnData,
                clumnDuration,
                clumnArtist,
                clumnAlbum,
                clumnDateAdded,
                clumnDateModified
            ),
            null, arrayOf(),
            null
        )
        try {
            while (cursor!!.moveToNext()) {
                val fileName = cursor.getString(cursor.getColumnIndex(clumnTitle))
                // String displayName = cursor.getString(cursor.getColumnIndex(clumnDisplayName));
                val filePath = cursor.getString(cursor.getColumnIndex(clumnData))
                val mimeType = cursor.getString(cursor.getColumnIndex(clumnMimeType))
                val size = cursor.getLong(cursor.getColumnIndex(clumnSize))
                val year = cursor.getString(cursor.getColumnIndex(clumnYear))
                val duration = cursor.getLong(cursor.getColumnIndex(clumnDuration))
                val artist = cursor.getString(cursor.getColumnIndex(clumnArtist))
                val album = cursor.getString(cursor.getColumnIndex(clumnAlbum))
                val dateAdded = cursor.getLong(cursor.getColumnIndex(clumnDateAdded))
                val dateModified = cursor.getLong(cursor.getColumnIndex(clumnDateModified))
                val item = AudioItem(filePath, fileName)
                item.size = size
                item.duration = duration
                item.year = year
                item.album = album
                item.artist = artist
                item.mimeType = mimeType
                item.dateAdded = dateAdded
                item.dateModified = dateModified
                list.add(item)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursor)
        }
        return list
    }

    /**
     * @features 功    能：查询图片
     * @method 方法名：queryImageItems
     * @param context
     * @return List<ImageItem>
     * @modify 修改者: caowg
    </ImageItem> */
    fun queryImageItems(): List<ImageItem> {
        val list: MutableList<ImageItem> = ArrayList()
        val uri = ImageDBConstant.URI
        val clumnId = ImageDBConstant._ID
        val clumnTitle = ImageDBConstant.TITLE
        val clumnData = ImageDBConstant.DATA
        val clumnSize = ImageDBConstant.SIZE
        val clumnMimeType = ImageDBConstant.MIME_TYPE
        val clumnDateAdded = ImageDBConstant.DATE_ADDED
        val clumnDateModified = ImageDBConstant.DATE_MODIFIED
        val clumnWidth = ImageDBConstant.WIDTH
        val clumnHeight = ImageDBConstant.HEIGHT
        val cursor = context.contentResolver
            .query(
                uri, arrayOf(
                    clumnId,
                    clumnTitle,
                    clumnMimeType,
                    clumnSize,
                    clumnData,
                    clumnDateAdded,
                    clumnDateModified,
                    clumnWidth,
                    clumnHeight
                ),
                null,
                null,
                null
            )
        try {
            while (cursor!!.moveToNext()) {
                val filePath = cursor.getString(cursor.getColumnIndex(clumnData))
                val fileName = cursor.getString(cursor.getColumnIndex(clumnTitle))
                val size = cursor.getLong(cursor.getColumnIndex(clumnSize))
                val mimeType = cursor.getString(cursor.getColumnIndex(clumnMimeType))
                val dateAdded = cursor.getLong(cursor.getColumnIndex(clumnDateAdded))
                val dateModified = cursor.getLong(cursor.getColumnIndex(clumnDateModified))
                val width = cursor.getInt(cursor.getColumnIndex(clumnWidth))
                val height = cursor.getInt(cursor.getColumnIndex(clumnHeight))
                val item = ImageItem(filePath, fileName)
                item.dateAdded = dateAdded
                item.dateModified = dateModified
                item.height = height
                item.size = size
                item.width = width
                item.mimeType = mimeType
                list.add(item)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursor)
        }
        return list
    }

    /**
     * 获取所有的缩列图
     *
     * @param context
     * @return
     */
    fun getBitmaps(): Array<Bitmap?>? {
        var bitmaps: Array<Bitmap?>? = null
        val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA)
        val cursor = context.contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            projection,
            null,
            null,
            MediaStore.Images.Media._ID
        )
        try {
            val count = cursor!!.count
            val image_column_index = cursor.getColumnIndex(MediaStore.Images.Media._ID)
            bitmaps = arrayOfNulls(count)
            for (i in 0 until count) {
                cursor.moveToPosition(i)
                val id = cursor.getInt(image_column_index)
                bitmaps[i] = MediaStore.Images.Thumbnails.getThumbnail(
                    context.contentResolver,
                    id.toLong(),
                    MediaStore.Images.Thumbnails.MICRO_KIND,
                    null
                )
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursor)
        }
        return bitmaps
    }

    /**
     * 查询图片缩列文件名称
     *
     * @param context
     * @return
     */
    fun getThumbNames(): List<String> {
        val list: MutableList<String> = ArrayList()
        val cursor = context.contentResolver.query(
            MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, arrayOf(
                MediaStore.Images.Thumbnails._ID,
                MediaStore.Images.Thumbnails.DATA,
                MediaStore.Images.Thumbnails.KIND,
                MediaStore.Images.Thumbnails.IMAGE_ID
            ),
            null, arrayOf(),
            null
        )
        try {
            while (cursor!!.moveToNext()) {
                val fileName =
                    cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME))
                list.add(fileName)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursor)
        }
        return list
    }

    /**
     * @features 功    能：查询视频文件
     * @method 方法名：queryVideoItems
     * @param context
     * @return List<VideoItem>
     * @modify 修改者: caowg
    </VideoItem> */
    fun queryVideoItems(): List<VideoItem> {
        val thumbColumns = arrayOf(
            VideoThumbnailsDBConstant.DATA, VideoThumbnailsDBConstant.VIDEO_ID
        )
        val clumnId = VideoDBConstant._ID
        val clumnData = VideoDBConstant.DATA
        val clumnTitle = VideoDBConstant.TITLE
        val clumnMimeType = VideoDBConstant.MIME_TYPE
        val clumnDuration = VideoDBConstant.DURATION
        val clumnSize = VideoDBConstant.SIZE
        val clumnResolution = VideoDBConstant.RESOLUTION
        val clumnDateAdded = VideoDBConstant.DATE_ADDED
        val clumnDateModified = VideoDBConstant.DATE_MODIFIED
        val mediaColumns = arrayOf(
            clumnId, clumnData,  // 文件路径
            clumnTitle,  // 文件名
            clumnMimeType,  // 视频类型
            clumnDuration,  // 播放时间
            clumnSize,  // 大小
            clumnResolution, clumnDateAdded, clumnDateModified
        )

        // 首先检索SDcard上所有的video
        val cursorVideo = context.contentResolver.query(
            VideoDBConstant.URI,
            mediaColumns, null, null, null
        )
        val videoList: MutableList<VideoItem> = ArrayList()
        var cursorThumb: Cursor? = null
        try {
            if (cursorVideo!!.moveToFirst()) {
                do {
                    val filePath =
                        cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnData))
                    val mimeType =
                        cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnMimeType))
                    val title = cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnTitle))
                    val size = cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnSize))
                    val resolution =
                        cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnResolution))
                    val duration =
                        cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnDuration))
                    val dateAdded =
                        cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnDateAdded))
                    val dateModified =
                        cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnDateModified))

                    // 获取当前Video对应的Id，然后根据该ID获取其Thumb
                    val id = cursorVideo.getInt(cursorVideo.getColumnIndexOrThrow(clumnId))
                    val selection = VideoThumbnailsDBConstant.VIDEO_ID + "=?"
                    val selectionArgs = arrayOf(id.toString() + "")
                    cursorThumb = context.contentResolver.query(
                        VideoThumbnailsDBConstant.URI,
                        thumbColumns, selection, selectionArgs, null
                    )
                    var thumbPath: String? = null
                    if (cursorThumb!!.moveToFirst()) {
                        thumbPath = cursorThumb.getString(
                            cursorThumb.getColumnIndexOrThrow(VideoThumbnailsDBConstant.DATA)
                        )
                    }
                    val item = VideoItem(filePath, title, thumbPath)
                    item.mimeType = mimeType
                    item.duration = duration
                    item.size = size
                    item.dateAdded = dateAdded
                    item.dateModified = dateModified
                    item.resolution = resolution
                    // 然后将其加入到videoList
                    videoList.add(item)
                } while (cursorVideo.moveToNext())
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursorVideo)
            closeCursor(cursorThumb)
        }
        return videoList
    }

    /**
     * @features 功    能：通知媒体库扫描sdcard，前提是文件必须已经在sdcard中已经存在
     * @param path
     * 已经存放到sdcard的文件的目录
     * @param context
     * @return void
     */
    @Deprecated("操作多媒体文件后会自动扫描，该方法多余")
    fun notifyMediaScanerScanFile(path: String?, context: Context) {
        context.sendBroadcast(
            Intent(
                Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
                Uri.fromFile(File(path))
            )
        )
    }

    /**
     * @features 功    能：通知扫描整个sdcard，如果sdcard的东西较多，将会很费时间，而且扫描未完成是不能访问sdcard的
     * @param context
     * @return void
     */
    @Deprecated("android Q 不需要")
    fun notifyMediaAll() {
        context.sendBroadcast(
            Intent(
                Intent.ACTION_MEDIA_MOUNTED,
                Uri.parse("file://" + Environment.getExternalStorageDirectory())
            )
        )
    }

    /**
     * @features 功    能：将图片插入媒体库并且刷新，前提必须图片已经保存到了sdcard
     * @param path
     * 已经存放到sdcard的文件的路径
     * @param name
     * 图片名称
     * @param description
     * 图片描述
     * @param context
     * @return void
     */
    fun insertImage(path: String?, name: String?, description: String?) {
        try {
            MediaStore.Images.Media.insertImage(context.contentResolver, path, name, description)
//            notifyMediaScanerScanFile(path, context)
        } catch (e: FileNotFoundException) {
            e.printStackTrace()
        }
    }

    /**
     * @features 功    能：扫描媒体文件并插入媒体库
     * @param context
     * @param pathAndMimeTypes
     * key存放path,value存放mimeType
     * @param callback
     * @return void
     */
    fun insert(
        context: Context?,
        pathAndMimeTypes: Map<String?, String?>?,
        callback: OnScanCompletedListener?
    ) {
        if (null == pathAndMimeTypes) {
            throw NullPointerException("pathAndMimeTypes == null")
        }
        val paths = pathAndMimeTypes.keys.toTypedArray() as Array<String>
        val mimeTypes = pathAndMimeTypes.values.toTypedArray() as Array<String>
        insert(context, paths, mimeTypes, callback)
    }

    /**
     * @features 功    能：扫描媒体文件并插入媒体库[失败]
     * @param context
     * @param paths
     * @param mimeTypes
     * @param callback
     * @return void
     */
    fun insert(
        context: Context?,
        paths: Array<String>?,
        mimeTypes: Array<String>?,
        callback: OnScanCompletedListener?
    ) {
        MediaScannerConnection.scanFile(context, paths, mimeTypes, callback)
    }

    /**
     * @features 功    能：删除在媒体库中的图片，并且将存放在sdcard中的图片删除
     * @param path
     * @param context
     * @return void
     */
    fun deleteImage(path: String, context: Context) {
        delete(ImageDBConstant.URI, ImageDBConstant.DATA, path, context)
    }

    /**
     * @features 功    能：删除
     * @param uri
     * @param columnData
     * 数据库中的路径列名
     * @param path
     * sdcard文件中的路径
     * @param context
     * @return void
     */
    private fun delete(uri: Uri, columnData: String, path: String, context: Context) {
        val where = "$columnData=?"
        val selectionArgs = arrayOf(path)
        context.contentResolver.delete(uri, where, selectionArgs)
    }

    /**
     * @features 功    能：删除在媒体库中音频，并且将存放在sdcard中的音频文件删除[失败]
     * @param path
     * @param context
     * @return void
     */
    fun deleteAudio(path: String, context: Context) {
        delete(AudioDBConstant.URI, AudioDBConstant.DATA, path, context)
    }

    /**
     * @features 功    能：删除在媒体库中的视频，并且将存放在sdcard中的视频文件删除
     * @param path
     * @param context
     * @return void
     */
    fun deleteVideo(path: String, context: Context) {
        delete(VideoDBConstant.URI, VideoDBConstant.DATA, path, context)
    }

    /**
     * 查询指定路径下的音频文件
     *
     * @param context
     * @param path
     * @return AudioItem
     */
    fun queryAudioItem(context: Context, path: String): AudioItem? {
        var item: AudioItem? = null
        val uri = AudioDBConstant.URI
        val clumnId = AudioDBConstant._ID
        val clumnTitle = AudioDBConstant.TITLE
        val clumnDisplayName = AudioDBConstant.DISPLAY_NAME
        val clumnData = AudioDBConstant.DATA
        val clumnMimeType = AudioDBConstant.MIME_TYPE
        val clumnSize = AudioDBConstant.SIZE
        val clumnYear = AudioDBConstant.YEAR
        val clumnDuration = AudioDBConstant.DURATION
        val clumnArtist = AudioDBConstant.ARTIST
        val clumnAlbum = AudioDBConstant.ALBUM
        val clumnDateAdded = AudioDBConstant.DATE_ADDED
        val clumnDateModified = AudioDBConstant.DATE_MODIFIED
        val projection = arrayOf(
            clumnId,
            clumnTitle,
            clumnDisplayName,
            clumnMimeType,
            clumnSize,
            clumnYear,
            clumnData,
            clumnDuration,
            clumnArtist,
            clumnAlbum,
            clumnDateAdded,
            clumnDateModified
        )
        val selection = "$clumnData=?"
        val selectionArgs = arrayOf(path)
        val cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null)
        try {
            while (cursor!!.moveToNext()) {
                val fileName = cursor.getString(cursor.getColumnIndex(clumnTitle))
                // String displayName = cursor.getString(cursor.getColumnIndex(clumnDisplayName));
                val filePath = cursor.getString(cursor.getColumnIndex(clumnData))
                val mimeType = cursor.getString(cursor.getColumnIndex(clumnMimeType))
                val size = cursor.getLong(cursor.getColumnIndex(clumnSize))
                val year = cursor.getString(cursor.getColumnIndex(clumnYear))
                val duration = cursor.getLong(cursor.getColumnIndex(clumnDuration))
                val artist = cursor.getString(cursor.getColumnIndex(clumnArtist))
                val album = cursor.getString(cursor.getColumnIndex(clumnAlbum))
                val dateAdded = cursor.getLong(cursor.getColumnIndex(clumnDateAdded))
                val dateModified = cursor.getLong(cursor.getColumnIndex(clumnDateModified))
                item = AudioItem(filePath, fileName)
                item.size = size
                item.duration = duration
                item.year = year
                item.album = album
                item.artist = artist
                item.mimeType = mimeType
                item.dateAdded = dateAdded
                item.dateModified = dateModified
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursor)
        }
        return item
    }

    /**
     * 查询指定路径下的视频文件的信息
     *
     * @param context
     * @param path
     * @return VideoItem
     */
    fun queryVideoItem(context: Context, path: String): VideoItem? {
        val thumbColumns = arrayOf(
            VideoThumbnailsDBConstant.DATA, VideoThumbnailsDBConstant.VIDEO_ID
        )
        val clumnId = VideoDBConstant._ID
        val clumnData = VideoDBConstant.DATA
        val clumnTitle = VideoDBConstant.TITLE
        val clumnMimeType = VideoDBConstant.MIME_TYPE
        val clumnDuration = VideoDBConstant.DURATION
        val clumnSize = VideoDBConstant.SIZE
        val clumnResolution = VideoDBConstant.RESOLUTION
        val clumnDateAdded = VideoDBConstant.DATE_ADDED
        val clumnDateModified = VideoDBConstant.DATE_MODIFIED
        val mediaColumns = arrayOf(
            clumnId, clumnData,  // 文件路径
            clumnTitle,  // 文件名
            clumnMimeType,  // 视频类型
            clumnDuration,  // 播放时间
            clumnSize,  // 大小
            clumnResolution, clumnDateAdded, clumnDateModified
        )
        val selectionVideo = "$clumnData=?"
        val selectionArgsVideo = arrayOf(path)
        val cursorVideo = context.contentResolver.query(
            VideoDBConstant.URI,
            mediaColumns,
            selectionVideo,
            selectionArgsVideo,
            null
        )
        var item: VideoItem? = null
        var cursorThumb: Cursor? = null
        try {
            if (cursorVideo!!.moveToFirst()) {
                do {
                    val filePath =
                        cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnData))
                    val mimeType =
                        cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnMimeType))
                    val title = cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnTitle))
                    val size = cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnSize))
                    val resolution =
                        cursorVideo.getString(cursorVideo.getColumnIndexOrThrow(clumnResolution))
                    val duration =
                        cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnDuration))
                    val dateAdded =
                        cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnDateAdded))
                    val dateModified =
                        cursorVideo.getLong(cursorVideo.getColumnIndexOrThrow(clumnDateModified))

                    // 获取当前Video对应的Id，然后根据该ID获取其Thumb
                    val id = cursorVideo.getInt(cursorVideo.getColumnIndexOrThrow(clumnId))
                    val selection = VideoThumbnailsDBConstant.VIDEO_ID + "=?"
                    val selectionArgs = arrayOf(id.toString() + "")
                    cursorThumb = context.contentResolver.query(
                        VideoThumbnailsDBConstant.URI,
                        thumbColumns, selection, selectionArgs, null
                    )
                    var thumbPath: String? = null
                    if (cursorThumb!!.moveToFirst()) {
                        thumbPath = cursorThumb.getString(
                            cursorThumb.getColumnIndexOrThrow(VideoThumbnailsDBConstant.DATA)
                        )
                    }
                    item = VideoItem(filePath, title, thumbPath)
                    item.mimeType = mimeType
                    item.duration = duration
                    item.size = size
                    item.dateAdded = dateAdded
                    item.dateModified = dateModified
                    item.resolution = resolution
                } while (cursorVideo.moveToNext())
            }
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            closeCursor(cursorThumb)
            closeCursor(cursorVideo)
        }
        return item
    }

    private fun closeCursor(cursor: Cursor?) {
        if (null != cursor) {
            if (!cursor.isClosed) {
                cursor.close()
            }
        }
    }
}